#include "PointLightsSampler.hpp"
#include <cmath>
#include "PenroseBasedSamplerConstants.hpp"


PointLightsSampler::PointLightsSampler(unsigned int iWidth, unsigned int iHeight)
{
	m_iWidth    = iWidth;
	m_iHeight   = iHeight;
	m_pRootNode = NULL;
}

PointLightsSampler::~PointLightsSampler()
{
	if(m_pRootNode)
	{
		delete m_pRootNode;
		m_pRootNode = NULL;
	}
}

bool PointLightsSampler::pointInBounds(MVector2D& point)
{
	bool bSuccess = false;

	if(point.x >= 0 && point.x < m_iWidth && point.y >= 0 && point.y < m_iHeight)
		bSuccess = true;

	return bSuccess;
}

unsigned int PointLightsSampler::getImportanceAtAndInBounds(MVector2D point)
{
	if(pointInBounds(point))
		return getImportanceAt(point);
	else
		return 0;
}

void PointLightsSampler::setSize(unsigned int iWidth, unsigned int iHeight)
{
	m_iWidth = iWidth;
	m_iHeight = iHeight;
}

void PointLightsSampler::getSize(unsigned int* pWidth, unsigned int* pHeight)
{
	*pWidth = m_iWidth; *pHeight = m_iHeight;
}

void PointLightsSampler::subdivideAll(int iIterations)
{
	if(!m_pRootNode)
		return;

	TileNode* pTmp;

	for(int i = 0; i < iIterations; ++i)
	{
		TileLeafIterator iterator(m_pRootNode);

		do
		{
			pTmp = *iterator;
			iterator.next();
			pTmp->divide();

		} while(*iterator);
	}
}

void PointLightsSampler::buildAdaptiveSubdivision(unsigned int iMinSubdivLvl)
{
	m_pRootNode = new TileNode(m_iWidth, m_iHeight);

	subdivideAll(iMinSubdivLvl);

	TileLeafIterator iterator(m_pRootNode);
	TileNode* pTmp;

	unsigned int level;

	do
	{
		level = iterator->getDepthLvl();

		if(iterator->isSamplingType())
		{
			if(level < getRequiredSubdivLvl(getImportanceAtAndInBounds(iterator->getTriangle().getVertexA())))
			{
				pTmp = *iterator;
				pTmp->divide();
			}
			else
			{
				if(
					(level <getRequiredSubdivLvl(getImportanceAtAndInBounds(iterator->getTriangle().getVertexA()))) ||
					(level <getRequiredSubdivLvl(getImportanceAtAndInBounds(iterator->getTriangle().getVertexB()))) ||
					(level <getRequiredSubdivLvl(getImportanceAtAndInBounds(iterator->getTriangle().getVertexC()))) ||
					(level <getRequiredSubdivLvl(getImportanceAtAndInBounds(iterator->getTriangle().getCenter())))
					)
				{
					pTmp = *iterator;
					pTmp->divide();
				}
			}
		}

	} while(iterator.next());
}

void PointLightsSampler::gatherPoints(samplingPoints& points, bool bFilterBounds)
{
	MVector2D      point, pointDisplaced;
	unsigned int   iImportance;

	TileLeafIterator iterator(m_pRootNode);

	do
	{
		point = iterator->getTriangle().getVertexA();

		if(iterator->isSamplingType())
		{
			iImportance = getImportanceAtAndInBounds(point);

			if(iImportance >= getFCodeValue(iterator->getFCode(), 2 * iterator->getDepthLvl()))
			{
				pointDisplaced = iterator->getDisplacedSamplingPoint(iImportance);

				if(!bFilterBounds || pointInBounds(pointDisplaced))
					points.push_back(pointDisplaced);
			}
		}

	} while(iterator.next());
}

samplingPoints PointLightsSampler::getSamplingPoints()
{
	if(m_pRootNode)
	{
		delete m_pRootNode;
		m_pRootNode = NULL;
	}

	samplingPoints pointsVector;

	buildAdaptiveSubdivision();
	gatherPoints(pointsVector);

	return pointsVector;
}

unsigned int PointLightsSampler::getFibonacciNumber(unsigned int iPosition)
{
	if(iPosition < 1)
		return 1;

	if(iPosition <= 64)
		return m_fibTable[iPosition - 1];

	return getFibonacciNumber(iPosition - 1) + getFibonacciNumber(iPosition - 2);
}

unsigned int PointLightsSampler::getRequiredSubdivLvl(unsigned int iImportance)
{
	if(iImportance == 0)
		return 0;

	unsigned nbits = (unsigned)(log((double)iImportance * sqrt(5.0) + 1.0) / LOG_PHI) - 1;

	if(nbits < 1)
		nbits = 1;

	return (unsigned)ceil(0.5*nbits);
}

unsigned int PointLightsSampler::getFCodeValue(unsigned int iBits, unsigned int iNumBits)
{
	unsigned int iCode = 0;

	for(unsigned int i = 0; i < iNumBits; ++i)
	{
		if(iBits & (1u << (iNumBits - i - 1)))
			iCode += getFibonacciNumber(i+2);
	}

	return iCode;
}

unsigned int PointLightsSampler::getStructuralIdx(unsigned int iBits)
{
	return getFCodeValue(iBits, NUM_STRUCT_INDEX_BITS);
}

unsigned int PointLightsSampler::getImportanceIdx(unsigned int iImportance)
{
	double t = log(1.0 + sqrt(5.0) * iImportance) / log(PHI2);

	t -= floor(t); //modulo 1.0
	return static_cast<unsigned int>(LUT_SIZE * t);
}

MVector2D PointLightsSampler::getDisplacementVec(unsigned int iImportance, unsigned int iFCode, unsigned int iDirection)
{
	unsigned int iStructIdx = getStructuralIdx(iFCode);
	unsigned int iImportIdx = getImportanceIdx(iImportance);

	return 
		vvect[iDirection]        * lut[iImportIdx][iStructIdx][0] + //u component
		vvect[(iDirection+5)%20] * lut[iImportIdx][iStructIdx][1];  //v component
}

//EOF
